package com.xuecheng.service.impl;

import com.alibaba.fastjson.JSON;
import com.xuecheng.model.PageParams;
import com.xuecheng.model.dto.SearchCourseParamDTO;
import com.xuecheng.model.po.CourseIndex;
import com.xuecheng.model.vo.SearchPageResultVO;
import com.xuecheng.service.CourseSearchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class CourseSearchServiceImpl implements CourseSearchService {
	
	@Resource
	private RestHighLevelClient client;
	@Value("${elasticsearch.course.index}")
	private String courseIndexStore;
	@Value("${elasticsearch.course.source_fields}")
	private String sourceFields;
	
	@Override
	public SearchPageResultVO<CourseIndex> queryCoursePubIndex(PageParams pageParams,
	                                                           SearchCourseParamDTO searchCourseParamDTO) {
		
		//设置索引
		SearchRequest searchRequest = new SearchRequest(courseIndexStore);
		
		SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
		BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
		//source源字段过虑
		String[] sourceFieldsArray = sourceFields.split(",");
		searchSourceBuilder.fetchSource(sourceFieldsArray, new String[]{});
		if (searchCourseParamDTO == null) {
			searchCourseParamDTO = new SearchCourseParamDTO();
		}
		//关键字
		if (StringUtils.isNotEmpty(searchCourseParamDTO.getKeywords())) {
			//匹配关键字
			MultiMatchQueryBuilder multiMatchQueryBuilder =
				QueryBuilders.multiMatchQuery(searchCourseParamDTO.getKeywords(), "name", "description");
			//设置匹配占比
			multiMatchQueryBuilder.minimumShouldMatch("70%");
			//提升另个字段的Boost值
			multiMatchQueryBuilder.field("name", 10);
			boolQueryBuilder.must(multiMatchQueryBuilder);
		}
		//过虑
		if (StringUtils.isNotEmpty(searchCourseParamDTO.getMt())) {
			boolQueryBuilder.filter(QueryBuilders.termQuery("mtName", searchCourseParamDTO.getMt()));
		}
		if (StringUtils.isNotEmpty(searchCourseParamDTO.getSt())) {
			boolQueryBuilder.filter(QueryBuilders.termQuery("stName", searchCourseParamDTO.getSt()));
		}
		if (StringUtils.isNotEmpty(searchCourseParamDTO.getGrade())) {
			boolQueryBuilder.filter(QueryBuilders.termQuery("grade", searchCourseParamDTO.getGrade()));
		}
		//分页
		Long pageNo = pageParams.getPageNo();
		Long pageSize = pageParams.getPageSize();
		int start = (int) ((pageNo - 1) * pageSize);
		searchSourceBuilder.from(start);
		searchSourceBuilder.size(Math.toIntExact(pageSize));
		//布尔查询
		searchSourceBuilder.query(boolQueryBuilder);
		//高亮设置
		HighlightBuilder highlightBuilder = new HighlightBuilder();
		highlightBuilder.preTags("<font class='eslight'>");
		highlightBuilder.postTags("</font>");
		//设置高亮字段
		highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
		searchSourceBuilder.highlighter(highlightBuilder);
		//请求搜索
		searchRequest.source(searchSourceBuilder);
		//聚合设置
		buildAggregation(searchRequest);
		SearchResponse searchResponse = null;
		try {
			searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
		} catch (IOException e) {
			log.error("课程搜索异常：{}", e.getMessage());
			return new SearchPageResultVO<CourseIndex>(new ArrayList<>(), 0, 0, 0);
		}
		
		//结果集处理
		SearchHits hits = searchResponse.getHits();
		SearchHit[] searchHits = hits.getHits();
		//记录总数
		TotalHits totalHits = hits.getTotalHits();
		//数据列表
		List<CourseIndex> list = new ArrayList<>();
		
		for (SearchHit hit : searchHits) {
			
			String sourceAsString = hit.getSourceAsString();
			CourseIndex courseIndex = JSON.parseObject(sourceAsString, CourseIndex.class);
			
			//取出source
//			Map<String, Object> sourceAsMap = hit.getSourceAsMap();
			//课程id
			Long id = courseIndex.getId();
			//取出名称
			String name = courseIndex.getName();
			//取出高亮字段内容
			Map<String, HighlightField> highlightFields = hit.getHighlightFields();
			if (highlightFields != null) {
				HighlightField nameField = highlightFields.get("name");
				if (nameField != null) {
					Text[] fragments = nameField.getFragments();
					StringBuilder stringBuffer = new StringBuilder();
					for (Text str : fragments) {
						stringBuffer.append(str.string());
					}
					name = stringBuffer.toString();
					
				}
			}
			courseIndex.setId(id);
			courseIndex.setName(name);
			
			list.add(courseIndex);
			
		}
		SearchPageResultVO<CourseIndex> pageResult = new SearchPageResultVO<>(list, totalHits.value, pageNo,
			pageSize);
		
		//获取聚合结果
		List<String> mtList = getAggregation(searchResponse.getAggregations(), "mtAgg");
		List<String> stList = getAggregation(searchResponse.getAggregations(), "stAgg");
		
		pageResult.setMtList(mtList);
		pageResult.setStList(stList);
		
		return pageResult;
	}
	
	
	private void buildAggregation(SearchRequest request) {
		request.source().aggregation(AggregationBuilders
			.terms("mtAgg")
			.field("mtName")
			.size(100)
		);
		request.source().aggregation(AggregationBuilders
			.terms("stAgg")
			.field("stName")
			.size(100)
		);
		
	}
	
	private List<String> getAggregation(Aggregations aggregations, String aggName) {
		// 4.1.根据聚合名称获取聚合结果
		Terms brandTerms = aggregations.get(aggName);
		// 4.2.获取buckets
		List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
		// 4.3.遍历
		List<String> brandList = new ArrayList<>();
		for (Terms.Bucket bucket : buckets) {
			// 4.4.获取key
			String key = bucket.getKeyAsString();
			brandList.add(key);
		}
		return brandList;
	}
}
